Final Project
Section 1: Objective
For this project, I will be showing the log2(fold-change) in differential gene expression for RNA-seq data in two subsets of colon cancer: adenocarcinoma and cystic, mucinous and serous neoplasms. The final deliverable will be the the Glimma Vignette. The input files for the Glimma vignette will be HTSeq-count data obtained from The Cancer Genome Atlas Program (TCGA). I will be turning in an HTML containing my R Notebook.
Section 2: Datasets
Loading data
Data is obtained from TCGA. I filtered for RNA-Seq experimental strategy, TXT data format and HTSeq-counts workflow type. HTSeq-counts is a tool that quantifies the aligned reads overlapping a gene’s exons. HTSeq data does not have a header, is tab-delimited, the first column is the Ensembl gene ID and the second column is the number of mapped reads of the gene. The counts will be used in differential gene expression analysis using edgeR as the method. To look at the differential gene expression, the counts will be normalized using the calcNormFactors in edgeR and only reads that unambigously map to one gene are used.
RAW: URL for cystic, mucinous, and serous neoplasms, I will choose 30:
RAW: URL for adenocarcinoma, I will choose 30:
Unit test for data
The first 15 genes listed in the first column are:
ENSG00000000003.13
ENSG00000000005.5
ENSG00000000419.11
ENSG00000000457.12
ENSG00000000460.15
ENSG00000000938.11
ENSG00000000971.14
ENSG00000001036.12
ENSG00000001084.9
ENSG00000001167.13
ENSG00000001460.16
ENSG00000001461.15
ENSG00000001497.15
ENSG00000001561.6
ENSG00000001617.10
The last 15 lines listed in the first column are:
ENSGR0000263980.4
ENSGR0000264510.4
ENSGR0000264819.4
ENSGR0000265658.4
ENSGR0000270726.4
ENSGR0000275287.3
ENSGR0000276543.3
ENSGR0000277120.3
ENSGR0000280767.1
ENSGR0000281849.1
__no_feature
__ambiguous
__too_low_aQual
__not_aligned
__alignment_not_unique
Unit test for normalized data
Make a box plots of unnormalized and normalized data
par(mfrow=c(1,2))
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="A. Example: Unnormalised data",ylab="Log-cpm")
x2 <- calcNormFactors(x2)
x2$samples$norm.factors
Proposed Analysis
This project will compute and analyze the logarithmic ratio of differential gene expression of two subtypes of colon cancer. EdgeR will be used to import, organize, and normalize the data, Mus.musculus will be used for gene annotions, limma will be used to examine the gene expression anaylsis and make exploratory plots, Glimma will be used to make these plots interactive. RColorBrewer and gplots will be used to make heatmaps.
Flowchart
library(DiagrammeR)
grViz("digraph flowchart {
# node definitions with substituted label text
node [fontname = Helvetica, shape = rectangle]
tab1 [label = '@@1']
tab2 [label = '@@2']
tab3 [label = '@@3']
tab4 [label = '@@4']
tab5 [label = '@@5']
tab6 [label = '@@6']
tab7 [label = '@@7']
tab8 [label = '@@8']
tab9 [label = '@@9']
tab10 [label = '@@10']
# edge definitions with the node IDs
tab1 -> tab2;
tab2 -> tab3;
tab3 -> tab4;
tab4 -> tab8 -> tab5;
tab4 -> tab5 -> tab6 -> tab7 -> tab9 -> tab10
}
[1]: 'Download necessary libraries'
[2]: 'Load and read datasets'
[3]: 'Join datasets'
[4]: 'Unit test: Data was properly loaded?'
[5]: 'Normalize data'
[6]: 'Find Mean Varience Trend'
[7]: 'Analyze DE genes'
[8]: 'Troubleshoot and fix errors'
[9]: 'Make Interactive MDS plot'
[10]: 'Make HeatMap of log-CPM data'
")
NA
Loading libraries
library(limma)
library(Glimma)
library(edgeR)
library(Mus.musculus)
library(gplots)
library(RColorBrewer)
Proposed Timeline and Milestones
Week 1: Run the Glimma vignette. I will install the necessary packages in R and understand each step in the vignette.
Week 2: Load in the data (joins, creating datasets) and do a simple, 1 line unit test to look at the data. I will download 60 datasets (30 from each subtype) and join multiple datasets.
Week 3: Confirm that the data was loaded in correctly and analyze data using the Glimma vignette.
Week 4: Troubleshoot for additional errors and enhance the user interface.
User Interface
I anticipate having boxplots, heatmaps, and interactive multi-dimensional scaling (MDS) plots done in an R Notebook. I will submit an HTML page of my completed R Notebook.
MDS plot
library(limma)
lcpm <- cpm(x, log=TRUE)
par(mfrow=c(1,2))
col.group <- group
levels(col.group) <- brewer.pal(nlevels(col.group), "Set1")
col.group <- as.character(col.group)
col.lane <- lane
levels(col.lane) <- brewer.pal(nlevels(col.lane), "Set2")
col.lane <- as.character(col.lane)
plotMDS(lcpm, labels=group, col=col.group)
title(main="A. Sample groups")
plotMDS(lcpm, labels=lane, col=col.lane, dim=c(3,4))
title(main="B. Sequencing lanes")
Make MDS plot interactive
library(glimma)
glMDSPlot(lcpm, labels=paste(group, lane, sep="_"),
groups=x$samples[,c(2,5)], launch=FALSE)
HeatMap of log-CPM data
library(gplots)
basal.vs.lp.topgenes <- basal.vs.lp$ENTREZID[1:100]
i <- which(v$genes$ENTREZID %in% basal.vs.lp.topgenes)
mycol <- colorpanel(1000,"blue","white","red")
heatmap.2(lcpm[i,], scale="row",
labRow=v$genes$SYMBOL[i], labCol=group,
col=mycol, trace="none", density.info="none",
margin=c(8,6), lhei=c(2,10), dendrogram="column")
A script for setting up the Glimma Vignette
{ if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”) BiocManager::install(“limma”) library(limma)
if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”) BiocManager::install(“Glimma”) library(Glimma)
if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)
BiocManager::install(“edgeR”) library(edgeR)
if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)
BiocManager::install(“Mus.musculus”) library(Mus.musculus)
library(R.utils)
if (!requireNamespace(“BiocManager”, quietly = TRUE)) install.packages(“BiocManager”)
BiocManager::install(“CAMERA”) library(CAMERA) }
Data Packaging
Reading in count data
library(R.utils)
url <- "https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE63310&format=file"
utils::download.file(url, destfile="GSE63310_RAW.tar", mode="wb")
trying URL 'https://www.ncbi.nlm.nih.gov/geo/download/?acc=GSE63310&format=file'
Content type 'application/x-tar' length 1996800 bytes (1.9 MB)
==================================================
downloaded 1.9 MB
utils::untar("GSE63310_RAW.tar", exdir = ".")
files <- c("GSM1545535_10_6_5_11.txt", "GSM1545536_9_6_5_11.txt", "GSM1545538_purep53.txt",
"GSM1545539_JMS8-2.txt", "GSM1545540_JMS8-3.txt", "GSM1545541_JMS8-4.txt",
"GSM1545542_JMS8-5.txt", "GSM1545544_JMS9-P7c.txt", "GSM1545545_JMS9-P8c.txt")
for(i in paste(files, ".gz", sep=""))
R.utils::gunzip(i, overwrite=TRUE)
files <- c("GSM1545535_10_6_5_11.txt", "GSM1545536_9_6_5_11.txt",
"GSM1545538_purep53.txt", "GSM1545539_JMS8-2.txt",
"GSM1545540_JMS8-3.txt", "GSM1545541_JMS8-4.txt",
"GSM1545542_JMS8-5.txt", "GSM1545544_JMS9-P7c.txt",
"GSM1545545_JMS9-P8c.txt")
read.delim(files[1], nrow=5)
Reading the 9 text files into R and combining into a matrix of counts
library(edgeR)
x <- readDGE(files, columns=c(1,3))
class(x)
[1] "DGEList"
attr(,"package")
[1] "edgeR"
dim(x)
[1] 27179 9
Organizing gene annotations
library(Mus.musculus)
geneid <- rownames(x)
genes <- select(Mus.musculus, keys=geneid, columns=c("SYMBOL", "TXCHROM"),
keytype="ENTREZID")
'select()' returned 1:many mapping between keys and columns
head(genes)
Resolve duplicate gene IDs
library(Mus.musculus)
genes <- genes[!duplicated(genes$ENTREZID),]
Data Pre-processing
Remove lowly expressed genes
library(edgeR)
table(rowSums(x$counts==0)==9)
FALSE
16624
Filter genes while keeping as many genes as possible with worthwile counts
library(edgeR)
keep.exprs <- filterByExpr(x, group=group)
x <- x[keep.exprs,, keep.lib.sizes=FALSE]
dim(x)
[1] 16624 9
Plot the density of log-CPM values for raw and filtered data
lcpm.cutoff <- log2(10/M + 2/L)
library(RColorBrewer)
nsamples <- ncol(x)
col <- brewer.pal(nsamples, "Paired")
par(mfrow=c(1,2))
plot(density(lcpm[,1]), col=col[1], lwd=2, ylim=c(0,0.26), las=2, main="", xlab="")
title(main="A. Raw data", xlab="Log-cpm")
abline(v=lcpm.cutoff, lty=3)
for (i in 2:nsamples){
den <- density(lcpm[,i])
lines(den$x, den$y, col=col[i], lwd=2)
}
legend("topright", samplenames, text.col=col, bty="n")
lcpm <- cpm(x, log=TRUE)
plot(density(lcpm[,1]), col=col[1], lwd=2, ylim=c(0,0.26), las=2, main="", xlab="")
title(main="B. Filtered data", xlab="Log-cpm")
abline(v=lcpm.cutoff, lty=3)
for (i in 2:nsamples){
den <- density(lcpm[,i])
lines(den$x, den$y, col=col[i], lwd=2)
}
legend("topright", samplenames, text.col=col, bty="n")

Normalising gene expression distributions
library(edgeR)
x <- calcNormFactors(x, method = "TMM")
x$samples$norm.factors
[1] 0.8943956 1.0250186 1.0459005 1.0458455 1.0162707 0.9217132 0.9961959 1.0861026 0.9839203
Improve visualization by duplicating data then adjusting the counts
x2 <- x
x2$samples$norm.factors <- 1
x2$counts[,1] <- ceiling(x2$counts[,1]*0.05)
x2$counts[,2] <- x2$counts[,2]*5
Boxplot expression distribution of samples for normalised and unnormalised data
par(mfrow=c(1,2))
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="A. Example: Unnormalised data",ylab="Log-cpm")
x2 <- calcNormFactors(x2)
x2$samples$norm.factors
[1] 0.05770899 6.08287835 1.22023972 1.16478991 1.19661094 1.04659233 1.15048074 1.25431164 1.10901983
lcpm <- cpm(x2, log=TRUE)
boxplot(lcpm, las=2, col=col, main="")
title(main="B. Example: Normalised data",ylab="Log-cpm")

Unsupervised clustering of cells: make multi-dimensional scaling plot (MDS) to show simmilarities and dissimilarities between samples in an unsupervised manner
library(limma)
lcpm <- cpm(x, log=TRUE)
par(mfrow=c(1,2))
col.group <- group
levels(col.group) <- brewer.pal(nlevels(col.group), "Set1")
col.group <- as.character(col.group)
col.lane <- lane
levels(col.lane) <- brewer.pal(nlevels(col.lane), "Set2")
col.lane <- as.character(col.lane)
plotMDS(lcpm, labels=group, col=col.group)
title(main="A. Sample groups")
plotMDS(lcpm, labels=lane, col=col.lane, dim=c(3,4))
title(main="B. Sequencing lanes")
Make interactive using Glimma, html page will be generarted and opened in a browser if launch=TRUE
library(Glimma)
glMDSPlot(lcpm, labels=paste(group, lane, sep="_"),
groups=x$samples[,c(2,5)], launch=FALSE)
Differential Expression Analysis
Creating a design matrix
design <- model.matrix(~0+group+lane)
colnames(design) <- gsub("group", "", colnames(design))
design
Basal LP ML laneL006 laneL008
1 0 1 0 0 0
2 0 0 1 0 0
3 1 0 0 0 0
4 1 0 0 1 0
5 0 0 1 1 0
6 0 1 0 1 0
7 1 0 0 1 0
8 0 0 1 0 1
9 0 1 0 0 1
attr(,"assign")
[1] 1 1 1 2 2
attr(,"contrasts")
attr(,"contrasts")$group
[1] "contr.treatment"
attr(,"contrasts")$lane
[1] "contr.treatment"
Contrasts for pairwise comparisons between cell populations
library(Glimma)
contr.matrix <- makeContrasts(
BasalvsLP = Basal-LP,
BasalvsML = Basal - ML,
LPvsML = LP - ML,
levels = colnames(design))
contr.matrix
Contrasts
Levels BasalvsLP BasalvsML LPvsML
Basal 1 1 0
LP -1 0 1
ML 0 -1 -1
laneL006 0 0 0
laneL008 0 0 0
Remove heteroscedascity from count data
library(Glimma)
par(mfrow=c(1,2))
v <- voom(x, design, plot=TRUE)

v
An object of class "EList"
$genes
16619 more rows ...
$targets
$E
Samples
Tags 10_6_5_11 9_6_5_11 purep53 JMS8-2 JMS8-3 JMS8-4 JMS8-5 JMS9-P7c JMS9-P8c
497097 -4.292165 -3.856488 2.5185849 3.2931366 -4.459730 -3.994060 3.2869677 -3.2103696 -5.295316
20671 -4.292165 -4.593453 0.3560126 -0.4073032 -1.200995 -1.943434 0.8442767 -0.3228444 -1.207853
27395 3.876089 4.413107 4.5170045 4.5617546 4.344401 3.786363 3.8990635 4.3396075 4.124644
18777 4.708774 5.571872 5.3964008 5.1623650 5.649355 5.081611 5.0602470 5.7513694 5.142436
21399 4.785541 4.754537 5.3703795 5.1220551 4.869586 4.943840 5.1384776 5.0308985 4.979644
16619 more rows ...
$weights
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] 1.079413 1.332986 19.826915 20.273253 1.993686 1.395853 20.494977 1.107780 1.079413
[2,] 1.170357 1.456380 4.804866 8.660025 3.612508 2.626870 8.760149 3.211473 2.541942
[3,] 20.219073 25.573792 30.434759 28.528310 31.352260 25.743247 28.722497 21.200072 16.657930
[4,] 26.947557 32.505933 33.583128 33.232125 34.231754 32.354158 33.334340 30.348630 24.259801
[5,] 26.610864 28.501638 33.645479 33.206374 33.573492 31.996623 33.308490 25.171513 23.573305
16619 more rows ...
$design
Basal LP ML laneL006 laneL008
1 0 1 0 0 0
2 0 0 1 0 0
3 1 0 0 0 0
4 1 0 0 1 0
5 0 0 1 1 0
6 0 1 0 1 0
7 1 0 0 1 0
8 0 0 1 0 1
9 0 1 0 0 1
attr(,"assign")
[1] 1 1 1 2 2
attr(,"contrasts")
attr(,"contrasts")$group
[1] "contr.treatment"
attr(,"contrasts")$lane
[1] "contr.treatment"
Apply voom precision weights to data
vfit <- lmFit(v, design)
vfit <- contrasts.fit(vfit, contrasts=contr.matrix)
efit <- eBayes(vfit)
plotSA(efit, main="Final model: Mean-variance trend")

Examine the number of DE genes
summary(decideTests(efit))
BasalvsLP BasalvsML LPvsML
Down 4648 4927 3135
NotSig 7113 7026 10972
Up 4863 4671 2517
Set a minimum log-fold change(log-FC) of 1
tfit <- treat(vfit, lfc=1)
dt <- decideTests(tfit)
summary(dt)
BasalvsLP BasalvsML LPvsML
Down 1632 1777 224
NotSig 12976 12790 16210
Up 2016 2057 190
Extract and write results for all 3 comparisons (basalvsLP, basalvsML, and LPvsML) to a single output file
write.fit(tfit, dt, file="results.txt")
Examining individual DE genes from top to bottom
basal.vs.lp <- topTreat(tfit, coef=1, n=Inf)
basal.vs.ml <- topTreat(tfit, coef=2, n=Inf)
head(basal.vs.lp)
head(basal.vs.ml)
Summarize results for genes using mean-difference plots that highlight differentially expressed genes
plotMD(tfit, column=1, status=dt[,1], main=colnames(tfit)[1],
xlim=c(-8,13))

Make interactive mean-difference plot. To open html page in a browser make launch=TRUE.
glMDPlot(tfit, coef=1, status=dt, main=colnames(tfit)[1], side.main="ENTREZID", counts=lcpm, groups=group, launch=TRUE)
Make heatmap
library(gplots)
Attaching package: ‘gplots’
The following object is masked from ‘package:S4Vectors’:
space
The following object is masked from ‘package:stats’:
lowess
basal.vs.lp.topgenes <- basal.vs.lp$ENTREZID[1:100]
i <- which(v$genes$ENTREZID %in% basal.vs.lp.topgenes)
mycol <- colorpanel(1000,"blue","white","red")
heatmap.2(lcpm[i,], scale="row",
labRow=v$genes$SYMBOL[i], labCol=group,
col=mycol, trace="none", density.info="none",
margin=c(8,6), lhei=c(2,10), dendrogram="column")
Error in plot.new() : figure margins too large

Gene set testing by applying the camera method on c2 gene signatures from the Broad Institute’s MSigDB c2 collection
load("/Users/ashleynoriega/Downloads/mouse_c2_v5p2.rdata")
idx <- ids2indices(Mm.c2,id=rownames(v))
cam.BasalvsLP <- camera(v,idx,design,contrast=contr.matrix[,1])
head(cam.BasalvsLP,5)
cam.BasalvsML <- camera(v,idx,design,contrast=contr.matrix[,2])
head(cam.BasalvsML,5)
cam.LPvsML <- camera(v,idx,design,contrast=contr.matrix[,3])
head(cam.LPvsML,5)
barcodeplot(efit$t[,3], index=idx$LIM_MAMMARY_LUMINAL_MATURE_UP,
index2=idx$LIM_MAMMARY_LUMINAL_MATURE_DN, main="LPvsML")

LS0tCnRpdGxlOiAiVFJHTiA1MTAgRmluYWwgUHJvamVjdDogQ29sb24gQ2FuY2VyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIEZpbmFsIFByb2plY3QKCiMjIFNlY3Rpb24gMTogT2JqZWN0aXZlCkZvciB0aGlzIHByb2plY3QsIEkgd2lsbCBiZSBzaG93aW5nIHRoZSBsb2cyKGZvbGQtY2hhbmdlKSBpbiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIGZvciBSTkEtc2VxIGRhdGEgaW4gdHdvIHN1YnNldHMgb2YgY29sb24gY2FuY2VyOiBhZGVub2NhcmNpbm9tYSBhbmQgY3lzdGljLCBtdWNpbm91cyBhbmQgc2Vyb3VzIG5lb3BsYXNtcy4gVGhlIGZpbmFsIGRlbGl2ZXJhYmxlIHdpbGwgYmUgdGhlIHRoZSBHbGltbWEgVmlnbmV0dGUuIFRoZSBpbnB1dCBmaWxlcyBmb3IgdGhlIEdsaW1tYSB2aWduZXR0ZSB3aWxsIGJlIEhUU2VxLWNvdW50IGRhdGEgb2J0YWluZWQgZnJvbSBUaGUgQ2FuY2VyIEdlbm9tZSBBdGxhcyBQcm9ncmFtIChUQ0dBKS4gSSB3aWxsIGJlIHR1cm5pbmcgaW4gYW4gSFRNTCBjb250YWluaW5nIG15IFIgTm90ZWJvb2suCgojIyBTZWN0aW9uIDI6IERhdGFzZXRzCgojIyMgTG9hZGluZyBkYXRhCkRhdGEgaXMgb2J0YWluZWQgZnJvbSBUQ0dBLiBJIGZpbHRlcmVkIGZvciBSTkEtU2VxIGV4cGVyaW1lbnRhbCBzdHJhdGVneSwgVFhUIGRhdGEgZm9ybWF0IGFuZCBIVFNlcS1jb3VudHMgd29ya2Zsb3cgdHlwZS4gSFRTZXEtY291bnRzIGlzIGEgdG9vbCB0aGF0IHF1YW50aWZpZXMgdGhlIGFsaWduZWQgcmVhZHMgb3ZlcmxhcHBpbmcgYSBnZW5lJ3MgZXhvbnMuIEhUU2VxIGRhdGEgZG9lcyBub3QgaGF2ZSBhIGhlYWRlciwgaXMgdGFiLWRlbGltaXRlZCwgdGhlIGZpcnN0IGNvbHVtbiBpcyB0aGUgRW5zZW1ibCBnZW5lIElEIGFuZCB0aGUgc2Vjb25kIGNvbHVtbiBpcyB0aGUgbnVtYmVyIG9mIG1hcHBlZCByZWFkcyBvZiB0aGUgZ2VuZS4gVGhlIGNvdW50cyB3aWxsIGJlIHVzZWQgaW4gZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmFseXNpcyB1c2luZyBlZGdlUiBhcyB0aGUgbWV0aG9kLiBUbyBsb29rIGF0IHRoZSBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uLCB0aGUgY291bnRzIHdpbGwgYmUgbm9ybWFsaXplZCB1c2luZyB0aGUgY2FsY05vcm1GYWN0b3JzIGluIGVkZ2VSIGFuZCBvbmx5IHJlYWRzIHRoYXQgdW5hbWJpZ291c2x5IG1hcCB0byBvbmUgZ2VuZSBhcmUgdXNlZC4KClJBVzogW1VSTCBmb3IgY3lzdGljLCBtdWNpbm91cywgYW5kIHNlcm91cyBuZW9wbGFzbXMsIEkgd2lsbCBjaG9vc2UgMzA6XShodHRwczovL3BvcnRhbC5nZGMuY2FuY2VyLmdvdi9yZXBvc2l0b3J5P2ZpbHRlcnM9JTdCIm9wIiUzQSJhbmQiJTJDImNvbnRlbnQiJTNBJTVCJTdCImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJjYXNlcy5jYXNlX2lkIiUyQyJ2YWx1ZSIlM0ElNUIic2V0X2lkJTNBQVc0NU02QUtUdF9yTWJHZERha1QiJTVEJTdEJTJDIm9wIiUzQSJJTiIlN0QlMkMlN0Iib3AiJTNBImluIiUyQyJjb250ZW50IiUzQSU3QiJmaWVsZCIlM0EiY2FzZXMuZGlzZWFzZV90eXBlIiUyQyJ2YWx1ZSIlM0ElNUIiQ3lzdGljJTJDJTIwTXVjaW5vdXMlMjBhbmQlMjBTZXJvdXMlMjBOZW9wbGFzbXMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmFuYWx5c2lzLndvcmtmbG93X3R5cGUiJTJDInZhbHVlIiUzQSU1QiJIVFNlcSUyMC0lMjBDb3VudHMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmRhdGFfZm9ybWF0IiUyQyJ2YWx1ZSIlM0ElNUIiVFhUIiU1RCU3RCU3RCUyQyU3QiJvcCIlM0EiaW4iJTJDImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJmaWxlcy5leHBlcmltZW50YWxfc3RyYXRlZ3kiJTJDInZhbHVlIiUzQSU1QiJSTkEtU2VxIiU1RCU3RCU3RCU1RCU3RCZzZWFyY2hUYWJsZVRhYj1jYXNlcykKClJBVzogW1VSTCBmb3IgYWRlbm9jYXJjaW5vbWEsIEkgd2lsbCBjaG9vc2UgMzA6XShodHRwczovL3BvcnRhbC5nZGMuY2FuY2VyLmdvdi9yZXBvc2l0b3J5P2ZpbHRlcnM9JTdCIm9wIiUzQSJhbmQiJTJDImNvbnRlbnQiJTNBJTVCJTdCImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJjYXNlcy5jYXNlX2lkIiUyQyJ2YWx1ZSIlM0ElNUIic2V0X2lkJTNBQVc0NU02QUtUdF9yTWJHZERha1QiJTVEJTdEJTJDIm9wIiUzQSJJTiIlN0QlMkMlN0Iib3AiJTNBImluIiUyQyJjb250ZW50IiUzQSU3QiJmaWVsZCIlM0EiY2FzZXMuZGlzZWFzZV90eXBlIiUyQyJ2YWx1ZSIlM0ElNUIiQWRlbm9tYXMlMjBhbmQlMjBBZGVub2NhcmNpbm9tYXMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmFuYWx5c2lzLndvcmtmbG93X3R5cGUiJTJDInZhbHVlIiUzQSU1QiJIVFNlcSUyMC0lMjBDb3VudHMiJTVEJTdEJTdEJTJDJTdCIm9wIiUzQSJpbiIlMkMiY29udGVudCIlM0ElN0IiZmllbGQiJTNBImZpbGVzLmRhdGFfZm9ybWF0IiUyQyJ2YWx1ZSIlM0ElNUIiVFhUIiU1RCU3RCU3RCUyQyU3QiJvcCIlM0EiaW4iJTJDImNvbnRlbnQiJTNBJTdCImZpZWxkIiUzQSJmaWxlcy5leHBlcmltZW50YWxfc3RyYXRlZ3kiJTJDInZhbHVlIiUzQSU1QiJSTkEtU2VxIiU1RCU3RCU3RCU1RCU3RCZzZWFyY2hUYWJsZVRhYj1jYXNlcykKCiMjIyBVbml0IHRlc3QgZm9yIGRhdGEgClRoZSBmaXJzdCAxNSBnZW5lcyBsaXN0ZWQgaW4gdGhlIGZpcnN0IGNvbHVtbiBhcmU6IApgYGBgCkVOU0cwMDAwMDAwMDAwMy4xMwpFTlNHMDAwMDAwMDAwMDUuNQpFTlNHMDAwMDAwMDA0MTkuMTEKRU5TRzAwMDAwMDAwNDU3LjEyCkVOU0cwMDAwMDAwMDQ2MC4xNQpFTlNHMDAwMDAwMDA5MzguMTEKRU5TRzAwMDAwMDAwOTcxLjE0CkVOU0cwMDAwMDAwMTAzNi4xMgpFTlNHMDAwMDAwMDEwODQuOQpFTlNHMDAwMDAwMDExNjcuMTMKRU5TRzAwMDAwMDAxNDYwLjE2CkVOU0cwMDAwMDAwMTQ2MS4xNQpFTlNHMDAwMDAwMDE0OTcuMTUKRU5TRzAwMDAwMDAxNTYxLjYKRU5TRzAwMDAwMDAxNjE3LjEwCmBgYGAKVGhlIGxhc3QgMTUgbGluZXMgbGlzdGVkIGluIHRoZSBmaXJzdCBjb2x1bW4gYXJlOgpgYGBgCkVOU0dSMDAwMDI2Mzk4MC40CQpFTlNHUjAwMDAyNjQ1MTAuNAkKRU5TR1IwMDAwMjY0ODE5LjQJCkVOU0dSMDAwMDI2NTY1OC40CQpFTlNHUjAwMDAyNzA3MjYuNAkKRU5TR1IwMDAwMjc1Mjg3LjMJCkVOU0dSMDAwMDI3NjU0My4zCQpFTlNHUjAwMDAyNzcxMjAuMwkKRU5TR1IwMDAwMjgwNzY3LjEJCkVOU0dSMDAwMDI4MTg0OS4xCQpfX25vX2ZlYXR1cmUJCl9fYW1iaWd1b3VzCQpfX3Rvb19sb3dfYVF1YWwJCl9fbm90X2FsaWduZWQJCl9fYWxpZ25tZW50X25vdF91bmlxdWUJCmBgYGAKIyMjIFVuaXQgdGVzdCBmb3Igbm9ybWFsaXplZCBkYXRhCk1ha2UgYSBib3ggcGxvdHMgb2YgdW5ub3JtYWxpemVkIGFuZCBub3JtYWxpemVkIGRhdGEKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCmxjcG0gPC0gY3BtKHgyLCBsb2c9VFJVRSkKYm94cGxvdChsY3BtLCBsYXM9MiwgY29sPWNvbCwgbWFpbj0iIikKdGl0bGUobWFpbj0iQS4gRXhhbXBsZTogVW5ub3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQp4MiA8LSBjYWxjTm9ybUZhY3RvcnMoeDIpICAKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMKYGBgCgojIyBQcm9wb3NlZCBBbmFseXNpcwpUaGlzIHByb2plY3Qgd2lsbCBjb21wdXRlIGFuZCBhbmFseXplIHRoZSBsb2dhcml0aG1pYyByYXRpbyBvZiBkaWZmZXJlbnRpYWwgZ2VuZSBleHByZXNzaW9uIG9mIHR3byBzdWJ0eXBlcyBvZiBjb2xvbiBjYW5jZXIuIEVkZ2VSIHdpbGwgYmUgdXNlZCB0byBpbXBvcnQsIG9yZ2FuaXplLCBhbmQgbm9ybWFsaXplIHRoZSBkYXRhLCAgTXVzLm11c2N1bHVzIHdpbGwgYmUgdXNlZCBmb3IgZ2VuZSBhbm5vdGlvbnMsIGxpbW1hIHdpbGwgYmUgdXNlZCB0byBleGFtaW5lIHRoZSBnZW5lIGV4cHJlc3Npb24gYW5heWxzaXMgYW5kIG1ha2UgZXhwbG9yYXRvcnkgcGxvdHMsIEdsaW1tYSB3aWxsIGJlIHVzZWQgdG8gbWFrZSB0aGVzZSBwbG90cyBpbnRlcmFjdGl2ZS4gUkNvbG9yQnJld2VyIGFuZCBncGxvdHMgd2lsbCBiZSB1c2VkIHRvIG1ha2UgaGVhdG1hcHMuCgojIyMgRmxvd2NoYXJ0CmBgYHtyfQpsaWJyYXJ5KERpYWdyYW1tZVIpCmdyVml6KCJkaWdyYXBoIGZsb3djaGFydCB7CiAgICAgICMgbm9kZSBkZWZpbml0aW9ucyB3aXRoIHN1YnN0aXR1dGVkIGxhYmVsIHRleHQKICAgICAgbm9kZSBbZm9udG5hbWUgPSBIZWx2ZXRpY2EsIHNoYXBlID0gcmVjdGFuZ2xlXSAgICAgICAgCiAgICAgIHRhYjEgW2xhYmVsID0gJ0BAMSddCiAgICAgIHRhYjIgW2xhYmVsID0gJ0BAMiddCiAgICAgIHRhYjMgW2xhYmVsID0gJ0BAMyddCiAgICAgIHRhYjQgW2xhYmVsID0gJ0BANCddCiAgICAgIHRhYjUgW2xhYmVsID0gJ0BANSddCiAgICAgIHRhYjYgW2xhYmVsID0gJ0BANiddCiAgICAgIHRhYjcgW2xhYmVsID0gJ0BANyddCiAgICAgIHRhYjggW2xhYmVsID0gJ0BAOCddCiAgICAgIHRhYjkgW2xhYmVsID0gJ0BAOSddCiAgICAgIHRhYjEwIFtsYWJlbCA9ICdAQDEwJ10KCiAgICAgICMgZWRnZSBkZWZpbml0aW9ucyB3aXRoIHRoZSBub2RlIElEcwogICAgICB0YWIxIC0+IHRhYjI7CiAgICAgIHRhYjIgLT4gdGFiMzsKICAgICAgdGFiMyAtPiB0YWI0OwogICAgICB0YWI0IC0+IHRhYjggLT4gdGFiNTsKICAgICAgdGFiNCAtPiB0YWI1IC0+IHRhYjYgLT4gdGFiNyAtPiB0YWI5IC0+IHRhYjEwCiAgICAgIH0KCiAgICAgIFsxXTogJ0Rvd25sb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMnCiAgICAgIFsyXTogJ0xvYWQgYW5kIHJlYWQgZGF0YXNldHMnCiAgICAgIFszXTogJ0pvaW4gZGF0YXNldHMnCiAgICAgIFs0XTogJ1VuaXQgdGVzdDogRGF0YSB3YXMgcHJvcGVybHkgbG9hZGVkPycKICAgICAgWzVdOiAnTm9ybWFsaXplIGRhdGEnCiAgICAgIFs2XTogJ0ZpbmQgTWVhbiBWYXJpZW5jZSBUcmVuZCcKICAgICAgWzddOiAnQW5hbHl6ZSBERSBnZW5lcycKICAgICAgWzhdOiAnVHJvdWJsZXNob290IGFuZCBmaXggZXJyb3JzJwogICAgICBbOV06ICdNYWtlIEludGVyYWN0aXZlIE1EUyBwbG90JwogICAgICBbMTBdOiAnTWFrZSBIZWF0TWFwIG9mIGxvZy1DUE0gZGF0YScKICAgICAgIikKCmBgYAoKCiMjIyBMb2FkaW5nIGxpYnJhcmllcwpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGlicmFyeShHbGltbWEpCmxpYnJhcnkoZWRnZVIpCmxpYnJhcnkoTXVzLm11c2N1bHVzKQpsaWJyYXJ5KGdwbG90cykKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmBgYAoKIyMgUHJvcG9zZWQgVGltZWxpbmUgYW5kIE1pbGVzdG9uZXMKV2VlayAxOiBSdW4gdGhlIEdsaW1tYSB2aWduZXR0ZS4gSSB3aWxsIGluc3RhbGwgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBpbiBSIGFuZCB1bmRlcnN0YW5kIGVhY2ggc3RlcCBpbiB0aGUgdmlnbmV0dGUuCgpXZWVrIDI6IExvYWQgaW4gdGhlIGRhdGEgKGpvaW5zLCBjcmVhdGluZyBkYXRhc2V0cykgYW5kIGRvIGEgc2ltcGxlLCAxIGxpbmUgdW5pdCB0ZXN0IHRvIGxvb2sgYXQgdGhlIGRhdGEuIEkgd2lsbCBkb3dubG9hZCA2MCBkYXRhc2V0cyAoMzAgZnJvbSBlYWNoIHN1YnR5cGUpIGFuZCBqb2luIG11bHRpcGxlIGRhdGFzZXRzLgoKV2VlayAzOiBDb25maXJtIHRoYXQgdGhlIGRhdGEgd2FzIGxvYWRlZCBpbiBjb3JyZWN0bHkgYW5kIGFuYWx5emUgZGF0YSB1c2luZyB0aGUgR2xpbW1hIHZpZ25ldHRlLgoKV2VlayA0OiBUcm91Ymxlc2hvb3QgZm9yIGFkZGl0aW9uYWwgZXJyb3JzIGFuZCBlbmhhbmNlIHRoZSB1c2VyIGludGVyZmFjZS4KCiMjIFVzZXIgSW50ZXJmYWNlCkkgYW50aWNpcGF0ZSBoYXZpbmcgYm94cGxvdHMsIGhlYXRtYXBzLCBhbmQgaW50ZXJhY3RpdmUgbXVsdGktZGltZW5zaW9uYWwgc2NhbGluZyAoTURTKSBwbG90cyBkb25lIGluIGFuIFIgTm90ZWJvb2suIEkgd2lsbCBzdWJtaXQgYW4gSFRNTCBwYWdlIG9mIG15IGNvbXBsZXRlZCBSIE5vdGVib29rLgoKIyMjIE1EUyBwbG90CmBgYHtyfQpsaWJyYXJ5KGxpbW1hKQpsY3BtIDwtIGNwbSh4LCBsb2c9VFJVRSkKcGFyKG1mcm93PWMoMSwyKSkKY29sLmdyb3VwIDwtIGdyb3VwCmxldmVscyhjb2wuZ3JvdXApIDwtICBicmV3ZXIucGFsKG5sZXZlbHMoY29sLmdyb3VwKSwgIlNldDEiKQpjb2wuZ3JvdXAgPC0gYXMuY2hhcmFjdGVyKGNvbC5ncm91cCkKY29sLmxhbmUgPC0gbGFuZQpsZXZlbHMoY29sLmxhbmUpIDwtICBicmV3ZXIucGFsKG5sZXZlbHMoY29sLmxhbmUpLCAiU2V0MiIpCmNvbC5sYW5lIDwtIGFzLmNoYXJhY3Rlcihjb2wubGFuZSkKcGxvdE1EUyhsY3BtLCBsYWJlbHM9Z3JvdXAsIGNvbD1jb2wuZ3JvdXApCnRpdGxlKG1haW49IkEuIFNhbXBsZSBncm91cHMiKQpwbG90TURTKGxjcG0sIGxhYmVscz1sYW5lLCBjb2w9Y29sLmxhbmUsIGRpbT1jKDMsNCkpCnRpdGxlKG1haW49IkIuIFNlcXVlbmNpbmcgbGFuZXMiKQpgYGAKCiMjIyBNYWtlIE1EUyBwbG90IGludGVyYWN0aXZlCmBgYHtyfQpsaWJyYXJ5KGdsaW1tYSkKZ2xNRFNQbG90KGxjcG0sIGxhYmVscz1wYXN0ZShncm91cCwgbGFuZSwgc2VwPSJfIiksIAogICAgICAgICAgZ3JvdXBzPXgkc2FtcGxlc1ssYygyLDUpXSwgbGF1bmNoPUZBTFNFKQpgYGAKCiMjIyBIZWF0TWFwIG9mIGxvZy1DUE0gZGF0YQpgYGB7cn0KbGlicmFyeShncGxvdHMpCmJhc2FsLnZzLmxwLnRvcGdlbmVzIDwtIGJhc2FsLnZzLmxwJEVOVFJFWklEWzE6MTAwXQppIDwtIHdoaWNoKHYkZ2VuZXMkRU5UUkVaSUQgJWluJSBiYXNhbC52cy5scC50b3BnZW5lcykKbXljb2wgPC0gY29sb3JwYW5lbCgxMDAwLCJibHVlIiwid2hpdGUiLCJyZWQiKQpoZWF0bWFwLjIobGNwbVtpLF0sIHNjYWxlPSJyb3ciLAogICBsYWJSb3c9diRnZW5lcyRTWU1CT0xbaV0sIGxhYkNvbD1ncm91cCwgCiAgIGNvbD1teWNvbCwgdHJhY2U9Im5vbmUiLCBkZW5zaXR5LmluZm89Im5vbmUiLCAKICAgbWFyZ2luPWMoOCw2KSwgbGhlaT1jKDIsMTApLCBkZW5kcm9ncmFtPSJjb2x1bW4iKQpgYGAKCgojIEFzaGxleSBFIE5vcmllZ2EsCiMgTm92IDEzLCAyMDE5CiMgVFJHTiA1MTAgRmluYWwgUHJvamVjdDogTWlsZXN0b25lIDEKIyBBIHNjcmlwdCBmb3Igc2V0dGluZyB1cCB0aGUgR2xpbW1hIFZpZ25ldHRlCnsKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKQmlvY01hbmFnZXI6Omluc3RhbGwoImxpbW1hIikKbGlicmFyeShsaW1tYSkKCmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJHbGltbWEiKSAKbGlicmFyeShHbGltbWEpCgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJlZGdlUiIpCmxpYnJhcnkoZWRnZVIpCgppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkJpb2NNYW5hZ2VyIiwgcXVpZXRseSA9IFRSVUUpKQogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQoKQmlvY01hbmFnZXI6Omluc3RhbGwoIk11cy5tdXNjdWx1cyIpCmxpYnJhcnkoTXVzLm11c2N1bHVzKQoKbGlicmFyeShSLnV0aWxzKQoKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJDQU1FUkEiKQpsaWJyYXJ5KENBTUVSQSkKfQoKIyBEYXRhIFBhY2thZ2luZwoKIyMgUmVhZGluZyBpbiBjb3VudCBkYXRhCmBgYHtyfQpsaWJyYXJ5KFIudXRpbHMpCnVybCA8LSAiaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9nZW8vZG93bmxvYWQvP2FjYz1HU0U2MzMxMCZmb3JtYXQ9ZmlsZSIKdXRpbHM6OmRvd25sb2FkLmZpbGUodXJsLCBkZXN0ZmlsZT0iR1NFNjMzMTBfUkFXLnRhciIsIG1vZGU9IndiIikgCnV0aWxzOjp1bnRhcigiR1NFNjMzMTBfUkFXLnRhciIsIGV4ZGlyID0gIi4iKQpmaWxlcyA8LSBjKCJHU00xNTQ1NTM1XzEwXzZfNV8xMS50eHQiLCAiR1NNMTU0NTUzNl85XzZfNV8xMS50eHQiLCAiR1NNMTU0NTUzOF9wdXJlcDUzLnR4dCIsCiAgIkdTTTE1NDU1MzlfSk1TOC0yLnR4dCIsICJHU00xNTQ1NTQwX0pNUzgtMy50eHQiLCAiR1NNMTU0NTU0MV9KTVM4LTQudHh0IiwKICAiR1NNMTU0NTU0Ml9KTVM4LTUudHh0IiwgIkdTTTE1NDU1NDRfSk1TOS1QN2MudHh0IiwgIkdTTTE1NDU1NDVfSk1TOS1QOGMudHh0IikKZm9yKGkgaW4gcGFzdGUoZmlsZXMsICIuZ3oiLCBzZXA9IiIpKQogIFIudXRpbHM6Omd1bnppcChpLCBvdmVyd3JpdGU9VFJVRSkKZmlsZXMgPC0gYygiR1NNMTU0NTUzNV8xMF82XzVfMTEudHh0IiwgIkdTTTE1NDU1MzZfOV82XzVfMTEudHh0IiwKIkdTTTE1NDU1MzhfcHVyZXA1My50eHQiLCAiR1NNMTU0NTUzOV9KTVM4LTIudHh0IiwgCiAgICJHU00xNTQ1NTQwX0pNUzgtMy50eHQiLCAiR1NNMTU0NTU0MV9KTVM4LTQudHh0IiwgCiAgICJHU00xNTQ1NTQyX0pNUzgtNS50eHQiLCAiR1NNMTU0NTU0NF9KTVM5LVA3Yy50eHQiLCAKICAgIkdTTTE1NDU1NDVfSk1TOS1QOGMudHh0IikKcmVhZC5kZWxpbShmaWxlc1sxXSwgbnJvdz01KQpgYGAKCiMjIFJlYWRpbmcgdGhlIDkgdGV4dCBmaWxlcyBpbnRvIFIgYW5kIGNvbWJpbmluZyBpbnRvIGEgbWF0cml4IG9mIGNvdW50cwpgYGB7cn0KbGlicmFyeShlZGdlUikKeCA8LSByZWFkREdFKGZpbGVzLCBjb2x1bW5zPWMoMSwzKSkKY2xhc3MoeCkKZGltKHgpCmBgYAoKIyMgT3JnYW5pemluZyBzYW1wbGUgaW5mb3JtYXRpb24KYGBge3J9CmxpYnJhcnkoZWRnZVIpCnNhbXBsZW5hbWVzIDwtIHN1YnN0cmluZyhjb2xuYW1lcyh4KSwgMTIsIG5jaGFyKGNvbG5hbWVzKHgpKSkKc2FtcGxlbmFtZXMKY29sbmFtZXMoeCkgPC0gc2FtcGxlbmFtZXMKZ3JvdXAgPC0gYXMuZmFjdG9yKGMoIkxQIiwgIk1MIiwgIkJhc2FsIiwgIkJhc2FsIiwgIk1MIiwgIkxQIiwgCiAgICAgICAgICAgICAgICAgICAgICJCYXNhbCIsICJNTCIsICJMUCIpKQp4JHNhbXBsZXMkZ3JvdXAgPC0gZ3JvdXAKbGFuZSA8LSBhcy5mYWN0b3IocmVwKGMoIkwwMDQiLCJMMDA2IiwiTDAwOCIpLCBjKDMsNCwyKSkpCngkc2FtcGxlcyRsYW5lIDwtIGxhbmUKeCRzYW1wbGVzCmBgYAoKIyMgT3JnYW5pemluZyBnZW5lIGFubm90YXRpb25zCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKZ2VuZWlkIDwtIHJvd25hbWVzKHgpCmdlbmVzIDwtIHNlbGVjdChNdXMubXVzY3VsdXMsIGtleXM9Z2VuZWlkLCBjb2x1bW5zPWMoIlNZTUJPTCIsICJUWENIUk9NIiksIAogICAgICAgICAgICAgICAga2V5dHlwZT0iRU5UUkVaSUQiKQpoZWFkKGdlbmVzKQpgYGAKCiMjIFJlc29sdmUgZHVwbGljYXRlIGdlbmUgSURzCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKZ2VuZXMgPC0gZ2VuZXNbIWR1cGxpY2F0ZWQoZ2VuZXMkRU5UUkVaSUQpLF0KYGBgCgojIyBQYWNrYWdlIGluIGEgREdFTGlzdC1vYmplY3QgY29udGFpbmluZyByYXcgY291bnQgZGF0YSB3aXRoIGFzc29jaWF0ZWQgc2FtcGxlIGluZm9ybWF0aW9uIGFuZCBnZW5lIGFubm90YXRpb25zCmBgYHtyfQpsaWJyYXJ5KE11cy5tdXNjdWx1cykKeCRnZW5lcyA8LSBnZW5lcwp4CmBgYAoKIyBEYXRhIFByZS1wcm9jZXNzaW5nCgojIyBUcmFuc2Zvcm1hdGlvbnMgZnJvbSB0aGUgcmF3LXNjYWxlOiBjb252ZXJ0IHJhdyBjb3VudHMgdG8gY291bnRzIHBlciBtaWxsaW9uIChDUE0pIGFuZCBsb2cyLWNvdW50cyBwZXIgbWlsbGlvbiAobG9nLUNQTSkKYGBge3J9CmxpYnJhcnkoZWRnZVIpCmNwbSA8LSBjcG0oeCkKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCkwgPC0gbWVhbih4JHNhbXBsZXMkbGliLnNpemUpICogMWUtNgpNIDwtIG1lZGlhbih4JHNhbXBsZXMkbGliLnNpemUpICogMWUtNgpjKEwsIE0pCnN1bW1hcnkobGNwbSkKYGBgCgojIyBSZW1vdmUgbG93bHkgZXhwcmVzc2VkIGdlbmVzCmBgYHtyfQpsaWJyYXJ5KGVkZ2VSKQp0YWJsZShyb3dTdW1zKHgkY291bnRzPT0wKT09OSkKYGBgCgojIyBGaWx0ZXIgZ2VuZXMgd2hpbGUga2VlcGluZyBhcyBtYW55IGdlbmVzIGFzIHBvc3NpYmxlIHdpdGggd29ydGh3aWxlIGNvdW50cwpgYGB7cn0KbGlicmFyeShlZGdlUikKa2VlcC5leHBycyA8LSBmaWx0ZXJCeUV4cHIoeCwgZ3JvdXA9Z3JvdXApCnggPC0geFtrZWVwLmV4cHJzLCwga2VlcC5saWIuc2l6ZXM9RkFMU0VdCmRpbSh4KQpgYGAKCiMjIFBsb3QgdGhlIGRlbnNpdHkgb2YgbG9nLUNQTSB2YWx1ZXMgZm9yIHJhdyBhbmQgZmlsdGVyZWQgZGF0YQpgYGB7cn0KbGNwbS5jdXRvZmYgPC0gbG9nMigxMC9NICsgMi9MKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbnNhbXBsZXMgPC0gbmNvbCh4KQpjb2wgPC0gYnJld2VyLnBhbChuc2FtcGxlcywgIlBhaXJlZCIpCnBhcihtZnJvdz1jKDEsMikpCnBsb3QoZGVuc2l0eShsY3BtWywxXSksIGNvbD1jb2xbMV0sIGx3ZD0yLCB5bGltPWMoMCwwLjI2KSwgbGFzPTIsIG1haW49IiIsIHhsYWI9IiIpCnRpdGxlKG1haW49IkEuIFJhdyBkYXRhIiwgeGxhYj0iTG9nLWNwbSIpCmFibGluZSh2PWxjcG0uY3V0b2ZmLCBsdHk9MykKZm9yIChpIGluIDI6bnNhbXBsZXMpewpkZW4gPC0gZGVuc2l0eShsY3BtWyxpXSkKbGluZXMoZGVuJHgsIGRlbiR5LCBjb2w9Y29sW2ldLCBsd2Q9MikKfQpsZWdlbmQoInRvcHJpZ2h0Iiwgc2FtcGxlbmFtZXMsIHRleHQuY29sPWNvbCwgYnR5PSJuIikKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCnBsb3QoZGVuc2l0eShsY3BtWywxXSksIGNvbD1jb2xbMV0sIGx3ZD0yLCB5bGltPWMoMCwwLjI2KSwgbGFzPTIsIG1haW49IiIsIHhsYWI9IiIpCnRpdGxlKG1haW49IkIuIEZpbHRlcmVkIGRhdGEiLCB4bGFiPSJMb2ctY3BtIikKYWJsaW5lKHY9bGNwbS5jdXRvZmYsIGx0eT0zKQpmb3IgKGkgaW4gMjpuc2FtcGxlcyl7CmRlbiA8LSBkZW5zaXR5KGxjcG1bLGldKQpsaW5lcyhkZW4keCwgZGVuJHksIGNvbD1jb2xbaV0sIGx3ZD0yKQp9CmxlZ2VuZCgidG9wcmlnaHQiLCBzYW1wbGVuYW1lcywgdGV4dC5jb2w9Y29sLCBidHk9Im4iKQpgYGAKCiMjIE5vcm1hbGlzaW5nIGdlbmUgZXhwcmVzc2lvbiBkaXN0cmlidXRpb25zCmBgYHtyfQpsaWJyYXJ5KGVkZ2VSKQp4IDwtIGNhbGNOb3JtRmFjdG9ycyh4LCBtZXRob2QgPSAiVE1NIikKeCRzYW1wbGVzJG5vcm0uZmFjdG9ycwpgYGAKCiMjIEltcHJvdmUgdmlzdWFsaXphdGlvbiBieSBkdXBsaWNhdGluZyBkYXRhIHRoZW4gYWRqdXN0aW5nIHRoZSBjb3VudHMKYGBge3J9CngyIDwtIHgKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMgPC0gMQp4MiRjb3VudHNbLDFdIDwtIGNlaWxpbmcoeDIkY291bnRzWywxXSowLjA1KQp4MiRjb3VudHNbLDJdIDwtIHgyJGNvdW50c1ssMl0qNQpgYGAKCiMjIEJveHBsb3QgZXhwcmVzc2lvbiBkaXN0cmlidXRpb24gb2Ygc2FtcGxlcyBmb3Igbm9ybWFsaXNlZCBhbmQgdW5ub3JtYWxpc2VkIGRhdGEKYGBge3J9CnBhcihtZnJvdz1jKDEsMikpCmxjcG0gPC0gY3BtKHgyLCBsb2c9VFJVRSkKYm94cGxvdChsY3BtLCBsYXM9MiwgY29sPWNvbCwgbWFpbj0iIikKdGl0bGUobWFpbj0iQS4gRXhhbXBsZTogVW5ub3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQp4MiA8LSBjYWxjTm9ybUZhY3RvcnMoeDIpICAKeDIkc2FtcGxlcyRub3JtLmZhY3RvcnMKbGNwbSA8LSBjcG0oeDIsIGxvZz1UUlVFKQpib3hwbG90KGxjcG0sIGxhcz0yLCBjb2w9Y29sLCBtYWluPSIiKQp0aXRsZShtYWluPSJCLiBFeGFtcGxlOiBOb3JtYWxpc2VkIGRhdGEiLHlsYWI9IkxvZy1jcG0iKQpgYGAKCiMjIFVuc3VwZXJ2aXNlZCBjbHVzdGVyaW5nIG9mIGNlbGxzOiBtYWtlIG11bHRpLWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdCAoTURTKSB0byBzaG93IHNpbW1pbGFyaXRpZXMgYW5kIGRpc3NpbWlsYXJpdGllcyBiZXR3ZWVuIHNhbXBsZXMgaW4gYW4gdW5zdXBlcnZpc2VkIG1hbm5lcgpgYGB7cn0KbGlicmFyeShsaW1tYSkKbGNwbSA8LSBjcG0oeCwgbG9nPVRSVUUpCnBhcihtZnJvdz1jKDEsMikpCmNvbC5ncm91cCA8LSBncm91cApsZXZlbHMoY29sLmdyb3VwKSA8LSAgYnJld2VyLnBhbChubGV2ZWxzKGNvbC5ncm91cCksICJTZXQxIikKY29sLmdyb3VwIDwtIGFzLmNoYXJhY3Rlcihjb2wuZ3JvdXApCmNvbC5sYW5lIDwtIGxhbmUKbGV2ZWxzKGNvbC5sYW5lKSA8LSAgYnJld2VyLnBhbChubGV2ZWxzKGNvbC5sYW5lKSwgIlNldDIiKQpjb2wubGFuZSA8LSBhcy5jaGFyYWN0ZXIoY29sLmxhbmUpCnBsb3RNRFMobGNwbSwgbGFiZWxzPWdyb3VwLCBjb2w9Y29sLmdyb3VwKQp0aXRsZShtYWluPSJBLiBTYW1wbGUgZ3JvdXBzIikKcGxvdE1EUyhsY3BtLCBsYWJlbHM9bGFuZSwgY29sPWNvbC5sYW5lLCBkaW09YygzLDQpKQp0aXRsZShtYWluPSJCLiBTZXF1ZW5jaW5nIGxhbmVzIikKYGBgCgojIyBNYWtlIGludGVyYWN0aXZlIHVzaW5nIEdsaW1tYSwgaHRtbCBwYWdlIHdpbGwgYmUgZ2VuZXJhcnRlZCBhbmQgb3BlbmVkIGluIGEgYnJvd3NlciBpZiBsYXVuY2g9VFJVRQpgYGB7cn0KbGlicmFyeShHbGltbWEpCmdsTURTUGxvdChsY3BtLCBsYWJlbHM9cGFzdGUoZ3JvdXAsIGxhbmUsIHNlcD0iXyIpLCAKICAgICAgICAgIGdyb3Vwcz14JHNhbXBsZXNbLGMoMiw1KV0sIGxhdW5jaD1GQUxTRSkKCmBgYAoKIyBEaWZmZXJlbnRpYWwgRXhwcmVzc2lvbiBBbmFseXNpcwoKIyMgQ3JlYXRpbmcgYSBkZXNpZ24gbWF0cml4CmBgYHtyfQpkZXNpZ24gPC0gbW9kZWwubWF0cml4KH4wK2dyb3VwK2xhbmUpCmNvbG5hbWVzKGRlc2lnbikgPC0gZ3N1YigiZ3JvdXAiLCAiIiwgY29sbmFtZXMoZGVzaWduKSkKZGVzaWduCmBgYAoKIyMgQ29udHJhc3RzIGZvciBwYWlyd2lzZSBjb21wYXJpc29ucyBiZXR3ZWVuIGNlbGwgcG9wdWxhdGlvbnMKYGBge3J9CmxpYnJhcnkoR2xpbW1hKQpjb250ci5tYXRyaXggPC0gbWFrZUNvbnRyYXN0cygKICAgQmFzYWx2c0xQID0gQmFzYWwtTFAsIAogICBCYXNhbHZzTUwgPSBCYXNhbCAtIE1MLCAKICAgTFB2c01MID0gTFAgLSBNTCwgCiAgIGxldmVscyA9IGNvbG5hbWVzKGRlc2lnbikpCmNvbnRyLm1hdHJpeApgYGAKCiMjIFJlbW92ZSBoZXRlcm9zY2VkYXNjaXR5IGZyb20gY291bnQgZGF0YQpgYGB7cn0KbGlicmFyeShHbGltbWEpCnBhcihtZnJvdz1jKDEsMikpCnYgPC0gdm9vbSh4LCBkZXNpZ24sIHBsb3Q9VFJVRSkKdgpgYGAKCiMjIEFwcGx5IHZvb20gcHJlY2lzaW9uIHdlaWdodHMgdG8gZGF0YQpgYGB7cn0KdmZpdCA8LSBsbUZpdCh2LCBkZXNpZ24pCnZmaXQgPC0gY29udHJhc3RzLmZpdCh2Zml0LCBjb250cmFzdHM9Y29udHIubWF0cml4KQplZml0IDwtIGVCYXllcyh2Zml0KQpwbG90U0EoZWZpdCwgbWFpbj0iRmluYWwgbW9kZWw6IE1lYW4tdmFyaWFuY2UgdHJlbmQiKQpgYGAKCgojIyBFeGFtaW5lIHRoZSBudW1iZXIgb2YgREUgZ2VuZXMKYGBge3J9CnN1bW1hcnkoZGVjaWRlVGVzdHMoZWZpdCkpCmBgYAoKIyMgU2V0IGEgbWluaW11bSBsb2ctZm9sZCBjaGFuZ2UobG9nLUZDKSBvZiAxCmBgYHtyfQp0Zml0IDwtIHRyZWF0KHZmaXQsIGxmYz0xKQpkdCA8LSBkZWNpZGVUZXN0cyh0Zml0KQpzdW1tYXJ5KGR0KQpgYGAKCiMjIEV4dHJhY3QgZ2VuZXMgdGhhdCBhcmUgREUgaW4gbXVsdGlwbGUgY29tcGFyaXNvbnMKYGBge3J9CmRlLmNvbW1vbiA8LSB3aGljaChkdFssMV0hPTAgJiBkdFssMl0hPTApCmxlbmd0aChkZS5jb21tb24pCmhlYWQodGZpdCRnZW5lcyRTWU1CT0xbZGUuY29tbW9uXSwgbj0yMCkKdmVubkRpYWdyYW0oZHRbLDE6Ml0sIGNpcmNsZS5jb2w9YygidHVycXVvaXNlIiwgInNhbG1vbiIpKQpgYGAKCiMjIEV4dHJhY3QgYW5kIHdyaXRlIHJlc3VsdHMgZm9yIGFsbCAzIGNvbXBhcmlzb25zIChiYXNhbHZzTFAsIGJhc2FsdnNNTCwgYW5kIExQdnNNTCkgdG8gYSBzaW5nbGUgb3V0cHV0IGZpbGUKYGBge3J9CndyaXRlLmZpdCh0Zml0LCBkdCwgZmlsZT0icmVzdWx0cy50eHQiKQpgYGAKCiMjIEV4YW1pbmluZyBpbmRpdmlkdWFsIERFIGdlbmVzIGZyb20gdG9wIHRvIGJvdHRvbQpgYGB7cn0KYmFzYWwudnMubHAgPC0gdG9wVHJlYXQodGZpdCwgY29lZj0xLCBuPUluZikKYmFzYWwudnMubWwgPC0gdG9wVHJlYXQodGZpdCwgY29lZj0yLCBuPUluZikKaGVhZChiYXNhbC52cy5scCkKYGBgCgpgYGB7cn0KaGVhZChiYXNhbC52cy5tbCkKYGBgCgojIyBTdW1tYXJpemUgcmVzdWx0cyBmb3IgZ2VuZXMgdXNpbmcgbWVhbi1kaWZmZXJlbmNlIHBsb3RzIHRoYXQgaGlnaGxpZ2h0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwpgYGB7cn0KcGxvdE1EKHRmaXQsIGNvbHVtbj0xLCBzdGF0dXM9ZHRbLDFdLCBtYWluPWNvbG5hbWVzKHRmaXQpWzFdLCAKICAgICAgIHhsaW09YygtOCwxMykpCmBgYAoKIyMgTWFrZSBpbnRlcmFjdGl2ZSBtZWFuLWRpZmZlcmVuY2UgcGxvdC4gVG8gb3BlbiBodG1sIHBhZ2UgaW4gYSBicm93c2VyIG1ha2UgbGF1bmNoPVRSVUUuCmBgYHtyfQpnbE1EUGxvdCh0Zml0LCBjb2VmPTEsIHN0YXR1cz1kdCwgbWFpbj1jb2xuYW1lcyh0Zml0KVsxXSwgICAgICAgICBzaWRlLm1haW49IkVOVFJFWklEIiwgY291bnRzPWxjcG0sIGdyb3Vwcz1ncm91cCwgbGF1bmNoPVRSVUUpCmBgYAoKIyMgTWFrZSBoZWF0bWFwCmBgYHtyfQpsaWJyYXJ5KGdwbG90cykKYmFzYWwudnMubHAudG9wZ2VuZXMgPC0gYmFzYWwudnMubHAkRU5UUkVaSURbMToxMDBdCmkgPC0gd2hpY2godiRnZW5lcyRFTlRSRVpJRCAlaW4lIGJhc2FsLnZzLmxwLnRvcGdlbmVzKQpteWNvbCA8LSBjb2xvcnBhbmVsKDEwMDAsImJsdWUiLCJ3aGl0ZSIsInJlZCIpCmhlYXRtYXAuMihsY3BtW2ksXSwgc2NhbGU9InJvdyIsCiAgIGxhYlJvdz12JGdlbmVzJFNZTUJPTFtpXSwgbGFiQ29sPWdyb3VwLCAKICAgY29sPW15Y29sLCB0cmFjZT0ibm9uZSIsIGRlbnNpdHkuaW5mbz0ibm9uZSIsIAogICBtYXJnaW49Yyg4LDYpLCBsaGVpPWMoMiwxMCksIGRlbmRyb2dyYW09ImNvbHVtbiIpCmBgYAoKIyBHZW5lIHNldCB0ZXN0aW5nIGJ5IGFwcGx5aW5nIHRoZSBjYW1lcmEgbWV0aG9kIG9uIGMyIGdlbmUgc2lnbmF0dXJlcyBmcm9tIHRoZSBCcm9hZCBJbnN0aXR1dGXigJlzIE1TaWdEQiBjMiBjb2xsZWN0aW9uCmBgYHtyfQpsb2FkKCIvVXNlcnMvYXNobGV5bm9yaWVnYS9Eb3dubG9hZHMvbW91c2VfYzJfdjVwMi5yZGF0YSIpCmlkeCA8LSBpZHMyaW5kaWNlcyhNbS5jMixpZD1yb3duYW1lcyh2KSkKY2FtLkJhc2FsdnNMUCA8LSBjYW1lcmEodixpZHgsZGVzaWduLGNvbnRyYXN0PWNvbnRyLm1hdHJpeFssMV0pCmhlYWQoY2FtLkJhc2FsdnNMUCw1KQpjYW0uQmFzYWx2c01MIDwtIGNhbWVyYSh2LGlkeCxkZXNpZ24sY29udHJhc3Q9Y29udHIubWF0cml4WywyXSkKaGVhZChjYW0uQmFzYWx2c01MLDUpCmNhbS5MUHZzTUwgPC0gY2FtZXJhKHYsaWR4LGRlc2lnbixjb250cmFzdD1jb250ci5tYXRyaXhbLDNdKQpoZWFkKGNhbS5MUHZzTUwsNSkKYGBgCgoKYGBge3J9CmJhcmNvZGVwbG90KGVmaXQkdFssM10sIGluZGV4PWlkeCRMSU1fTUFNTUFSWV9MVU1JTkFMX01BVFVSRV9VUCwgCiAgICAgICAgICAgIGluZGV4Mj1pZHgkTElNX01BTU1BUllfTFVNSU5BTF9NQVRVUkVfRE4sIG1haW49IkxQdnNNTCIpCmBgYAoK